﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CSA.Lib;

namespace CSA.Operator
{
    public class SelectionOperator : ICSOperator
    {          
        #region Fields

        private CSPopulation m_OperatePopulation=null;    
        String m_CurUsedFunctionName="";
        Hashtable m_FunctionNames = new Hashtable();    //Name-CSFunctionInfo

        #endregion  

        public SelectionOperator()
        {
            InitialParameters();
        }

        ~SelectionOperator()
        {           
        }

        #region Proerpties

        public Hashtable Get_Parameters(String nFunctionName)
        {
            String nValidateName="";
            if (nFunctionName.Trim() == "" || !m_FunctionNames.Contains(nFunctionName))
            {
                nValidateName = "DefaultSelectionFunction";
            }
            else
            {
                nValidateName = nFunctionName;
            }
            CSFuntionInfo nDefulatParas = (CSFuntionInfo)m_FunctionNames[nValidateName];
            return nDefulatParas.Parameters;
        }

        public bool UseCustomAntibodyOperator
        {
            get { return false; }
        }

        public String Name
        {
            get
            {
                return "SelectionOperator";
            }
        }

        public String Caption
        {
            get
            {
                return global::CSA.Operator.TextString.SelectionOperatorCaption;
            }
        }

        public String Description
        {
            get
            {
                return global::CSA.Operator.TextString.SelectionOperaterDes;
            }
        }

        public String Library
        {
            get
            {
                return "CSA.Operator";
            }
        }

        public OperatorType OperatorType
        {
            get
            {
                return OperatorType.enumSelectionOperator;
            }
        }
        public Hashtable FunctionNames
        {
            get { return m_FunctionNames; }
        }

        public CSFuntionInfo CustomAntibodyOperator
        {
            get { return null; }
            set { }
        }

        #endregion   

        public bool Execute(CSPopulation nOperatePopulation, ref String nErrorStr, String nMethodName = "")
        {
            m_OperatePopulation = nOperatePopulation;
            if (nMethodName.Trim() == "" || !m_FunctionNames.Contains(nMethodName))
            {
                m_CurUsedFunctionName = "DefaultSelectionFunction";
            }
            else
            {
                m_CurUsedFunctionName = nMethodName;
            }          
            if (m_CurUsedFunctionName == "DefaultSelectionFunction")
            {
                return ElitistSelection(ref nErrorStr);
            }
            return true;
        }

        public void InitialParameters()
        {
            m_FunctionNames.Clear();
            //Add the parameters of the ElitistSelection to the dictionary
            CSFuntionInfo m_DefaultFunctionInfo = new CSFuntionInfo();
            m_DefaultFunctionInfo.Caption = global::CSA.Operator.TextString.DefaultFunction;
            m_DefaultFunctionInfo.Description = global::CSA.Operator.TextString.SelectionOperaterDes;
            m_DefaultFunctionInfo.Name = "DefaultSelectionFunction";          
            float nDefaultClonalRate = 0.1F;  //
            Hashtable nStandardFunctionPara=new Hashtable();
            CSParameter MaturationRate = new CSParameter();
            string nParaName1 = "SelectionRatio";
            MaturationRate.Caption = global::CSA.Operator.TextString.SelectionRatio;
            MaturationRate.Description = global::CSA.Operator.TextString.SelectionRatioDes;
            MaturationRate.Name = nParaName1;
            MaturationRate.Value = nDefaultClonalRate;
            MaturationRate.Type = ParameterType.enumFloat;
            MaturationRate.DomainType = enumParasDomain.enumDomainExtent;
            string low1 = "0.1";
            string up1 = "1";
            List<string> nDomain = new List<string>();
            nDomain.Add(low1);
            nDomain.Add(up1);
            MaturationRate.DomainValues = nDomain;
            nStandardFunctionPara.Add(nParaName1, MaturationRate);
            m_DefaultFunctionInfo.Parameters = nStandardFunctionPara;
            m_FunctionNames.Add(m_DefaultFunctionInfo.Name, m_DefaultFunctionInfo);
        }

        private bool ElitistSelection(ref String nErrorStr)
        {
            try
            {
                CSFuntionInfo nDefulatParas = (CSFuntionInfo)m_FunctionNames[m_CurUsedFunctionName];
                CSParameter MaturationRate = (CSParameter)nDefulatParas.Parameters["SelectionRatio"];
                Double nMuturationCoe = Convert.ToDouble(MaturationRate.Value.ToString());
                int nAntibodyToCloneCount, nPopSize;
                CSPopulation nPop = m_OperatePopulation;
                List<CSAntibody> nMemoryPop = nPop.MemoryAntibodies;
                List<CSAntibody> nNonMemoryPop = nPop.Antibodies;
                List<CSAntibody> nNewPop = nPop.NewAntibodies;
                nNewPop.Clear();
                nPopSize = nPop.PopulationSize;
                nAntibodyToCloneCount = (int)((nMuturationCoe) * nPopSize);
                if (nAntibodyToCloneCount < 1)
                {
                    return false;
                }
                int nMemorySelectIt = 0;
                int nNonMemorySelectIt = 0;
                int nCurSelected = 0;
                while (nCurSelected < nAntibodyToCloneCount)
                {
                    if (nMemorySelectIt < nMemoryPop.Count && nNonMemorySelectIt < nNonMemoryPop.Count)
                    {
                        CSAntibody nCurSeleMermoy = nMemoryPop[nMemorySelectIt];
                        CSAntibody nCurNonSeleMermoy = nNonMemoryPop[nNonMemorySelectIt];
                        Double nAff1 = nCurSeleMermoy.Affinity;
                        Double nAff2 = nCurNonSeleMermoy.Affinity;
                        if (nAff1 > nAff2)
                        {
                            CSAntibody nNewAntibody = (CSAntibody)nCurSeleMermoy.Clone();
                            nNewPop.Add(nNewAntibody);
                            ++nMemorySelectIt;
                            nCurSelected++;
                        }
                        else
                        {
                            CSAntibody nNewAntibody = (CSAntibody)nCurNonSeleMermoy.Clone();
                            nNewPop.Add(nNewAntibody);
                            ++nNonMemorySelectIt;
                            nCurSelected++;
                        }
                    }
                    else if (nMemorySelectIt == nMemoryPop.Count())
                    {
                        if (nNonMemorySelectIt < nNonMemoryPop.Count())
                        {
                            ++nNonMemorySelectIt;
                            CSAntibody nNewAntibody = (CSAntibody)nNonMemoryPop[nNonMemorySelectIt].Clone();
                            nNewPop.Add(nNewAntibody);
                            nCurSelected++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else if (nNonMemorySelectIt == nNonMemoryPop.Count)
                    {
                        ++nMemorySelectIt;
                        CSAntibody nNewAntibody = (CSAntibody)nMemoryPop[nMemorySelectIt].Clone();
                        nNewPop.Add(nNewAntibody);
                        nCurSelected++;
                    }
                }
                nNewPop.Sort(new AntibodyCompare());
                return true;
            }
            catch(Exception ex)
            {
                nErrorStr=ex.Message;
                return false;
            }
        }

    }
}
